/**
 * This file is released under the GNU General Public License.
 * Refer to the COPYING file distributed with this package.
 *
 * Copyright (c) 2008-2009 WURFL-Pro srl
 */
package net.sourceforge.wurfl.core;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import net.sourceforge.wurfl.core.resource.DeviceNotInModelException;
import net.sourceforge.wurfl.core.resource.ModelDevice;
import net.sourceforge.wurfl.core.resource.WURFLModel;

import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This is a interface to WURFL introspection methods. It is mainly used from
 * developers. In general it is a facade class to {@link WURFLModel}.
 * 
 * @author WURFL-PRO SRL, Rome, Italy
 * @version $Id: WURFLUtils.java 1045 2009-03-09 15:52:53Z filippo.deluca $
 */
public class WURFLUtils {

	/** DeviceRepository used to access devices */
	private WURFLModel model;

	private DeviceProvider deviceProvider;

	/** Log */
	private static final Log logger = LogFactory.getLog(WURFLUtils.class);

	/**
	 * Create a WURFLUtils instance from WURFLModel.
	 * 
	 * @param model
	 *            The backed WURFLModel instance.
	 */
	public WURFLUtils(final WURFLModel model) {
		this(model, new DefaultDeviceProvider(model));
	}

	/**
	 * Create a WURFLUtils instance from WURFLModel.
	 * 
	 * @param model
	 *            The backend WURFLModel instance.
	 */
	public WURFLUtils(final WURFLModel model, DeviceProvider deviceProvider) {
		this.model = model;
		this.deviceProvider = deviceProvider;
	}

	/**
	 * Facade method of {@link WURFLModel#getVersion()}.
	 * <p>
	 * It returns the version of the WURFL repository (root resource + patches).
	 * </p>
	 * 
	 * @return String representing version of underlying WURFL repository.
	 */
	public String getVersion() {
		return model.getVersion();
	}

	/**
	 * Facade method of WURFLModel#isDeviceDefined(String). It returns if a
	 * device with given identifier is defined by the backend WURFLModel.
	 * 
	 * @param id
	 *            The identifier of probing device.
	 * @return True if the device is defined in backend WURFLModel, false
	 *         otherwise.
	 */
	public boolean isDeviceDefined(final String id) {

		Validate.notEmpty(id, "deviceId must be not null");

		return model.isDeviceDefined(id);
	}

	/**
	 * Facade method of WURFLModel#getDeviceById(String). It returns a
	 * ModelDevice with given identifier.
	 * 
	 * @param id
	 *            The identifier of the ModelDevice to retrieve.
	 * @return A ModelDevice instance.
	 * 
	 * @throws DeviceNotDefinedException
	 *             if the device with given identifier is not defined by backend
	 *             WURFLModel.
	 */
	public ModelDevice getModelDeviceById(final String id)
			throws DeviceNotDefinedException {

		Validate.notEmpty(id, "The id must be not null Set");

		return model.getDeviceById(id);
	}

	/**
	 * Facade method of WURFLModel#getDevices(Set). It returns ModelDevices with
	 * given identifiers.
	 * 
	 * @param A
	 *            Set of String containing identifiers of ModelDevices to
	 *            retrieve.
	 * @return Set of {@link ModelDevice}.
	 */
	public Set getModelDevices(final Set ids) throws DeviceNotDefinedException {

		Validate.notNull(ids, "The ids must be not null Set");
		Validate.noNullElements(ids,
				"The ids must not containing null elements");
		Validate.allElementsOfType(ids, String.class,
				"The ids must containing right devices id");

		return model.getDevices(ids);
	}

	/**
	 * It is facade method for WURFLModel#getAllDevicesId(). It returns all
	 * devices identifiers defined by backend WURFLModel.
	 * 
	 * @return Set of String representing ModelDevices identifiers.
	 */
	public Set getAllDevicesId() {

		return model.getAllDevicesId();
	}

	/**
	 * It is facade method for WURFLModel#getAllDevices() . It returns all
	 * ModelDevices defined by backend WURFLModel.
	 * 
	 * @return Set of ModelDevice.
	 */
	public Set getAllModelDevices() {

		return model.getAllDevices();
	}

	/**
	 * It is facade method of WURFLModel#getDeviceHierarchy(ModelDevice). It
	 * returns a ModelDevice hierarchy. To know how is hierarchy defined look at
	 * WURFLModel#getDeviceHierarchy(ModelDevice).
	 * 
	 * @param root
	 *            The ModelDevice root of hierarchy.
	 * @return A List representing the <code>root</code> ModelDevice hierarchy.
	 * @throws DeviceNotInModelException
	 *             If the <code>root</code> ModelDevice is not contained in
	 *             backend WURFLModel.
	 */
	public List getModelDeviceHierarchy(final ModelDevice root)
			throws DeviceNotInModelException {

		Validate.notNull(root, "The root ModelDevice must be not null");

		return model.getDeviceHierarchy(root);
	}

	/**
	 * It is a facade method of WURFLModel#getDeviceFallback(ModelDevice). It
	 * returns the target's fallback ModelDevice.
	 * 
	 * @param target
	 *            The ModelDevice <code>device</code> to get ModelDevice
	 *            fallback from.
	 * @return A ModelDevice instance, fallback of the <code>root</code>.
	 * @throws DeviceNotInModelException
	 *             If the <code>root</code> is not contained in backend
	 *             WURFLModel.
	 */
	public ModelDevice getModelDeviceFallback(final ModelDevice target)
			throws DeviceNotInModelException {

		Validate.notNull(target, "The target ModelDevice must be not null");

		return model.getDeviceFallback(target);
	}

	/**
	 * It is a facade method of WURFLModel#getDeviceAncestor(ModelDevice). It
	 * returns a ModelDevice is ancestor for the given device. To know how is
	 * defined ancestor look at WURFLModel#getDeviceAncestor(ModelDevice).
	 * 
	 * @param target
	 *            The device to get ancestor from.
	 * 
	 * @return A root ModelDevice instance.
	 * @throws DeviceNotInModelException
	 *             If the <code>target</code> ModelDevice is not contained in
	 *             backend WURFLModel.
	 */
	public ModelDevice getModelDeviceAncestor(ModelDevice target)
			throws DeviceNotInModelException {

		Validate.notNull(target, "The root ModelDevice must be not null");

		return model.getDeviceAncestor(target);
	}

	// Capabilities *******************************************************

	/**
	 * Facade method of WURFLModel#isCapabilityDefined(String). It returns if
	 * the given capability is defined in backend WURFLModel.
	 * 
	 * @param capability
	 *            The name of capability to check against.
	 * @return True if the given capability is defined in WURFL, false
	 *         otherwise.
	 * @throws IllegalArgumentException
	 *             if <code>capabilityName</code> is null.
	 */
	public boolean isCapabilityDefined(String capability) {

		Validate.notEmpty(capability, "The capabilityName must be not null");

		return model.isCapabilityDefined(capability);
	}

	/**
	 * Facade method of WURFLModel#getAllCapabilities(). It returns all the
	 * capabilities defined in the backend WURFLModel.
	 * 
	 * @return A Set of String containing all capabilities defined by backend
	 *         WURFLModel.
	 */
	public Set getAllCapabilities() {

		return model.getAllCapabilities();
	}

	/**
	 * Facade method of WURFLModel#getGroupByCapability(String). Return the
	 * identifier of group define the given capability.
	 * 
	 * @param capability
	 *            The name of capability.
	 * @return The defining group identifier.
	 * @throws CapabilityNotDefinedException
	 *             If the backend WURFLModel do not define the capability
	 *             <code>capability</code>.
	 */
	public String getGroupByCapability(String capability)
			throws CapabilityNotDefinedException {

		Validate.notEmpty(capability, "The capabilityName must be not null");

		return model.getGroupByCapability(capability);
	}

	/**
	 * It is facade method of
	 * WURFLModel#getDeviceWhereCapabilityIsDefined(ModelDevice, String). It
	 * returns the ModelDevice where a capability is defined upon the target
	 * ModelDevice hierarchy.
	 * 
	 * @param target
	 *            The ModelDevice from which start the search of the definition
	 *            of capability.
	 * @param capability
	 *            The capability identifier to search.
	 * @return A ModelDevice instances, in the target hierarchy, defining the
	 *         given capability
	 * @throws CapabilityNotDefinedException
	 *             If the given capability is not defined upon the target
	 *             hierarchy.
	 * @throws DeviceNotInModelException
	 *             If the <code>target</code> ModelDevice is not contained in
	 *             backend WURFLModel.
	 */
	public ModelDevice getModelDeviceWhereCapabilityIsDefined(
			final ModelDevice target, String capability)
			throws CapabilityNotDefinedException, DeviceNotInModelException {

		Validate.notNull(target, "The rootDevice must be not null Set");
		Validate.notEmpty(capability, "The capabilityName must be not null");

		return model.getDeviceWhereCapabilityIsDefined(target, capability);
	}

	// Groups *************************************************************

	/**
	 * It is a facade method of WURFLModel#isGroupDefined(String). It returns if
	 * a group with given identifier is defined in backend WURFLModel.
	 * 
	 * @param group
	 *            the group identifier to probe.
	 * @return True if the given group is defined, false otherwise.
	 */
	public boolean isGroupDefined(final String group) {

		Validate.notEmpty(group, "The groupName must be not null");

		return model.isGroupDefined(group);
	}

	/**
	 * It is a facade method of WURFLModel#getAllGroups(). It return all
	 * identifiers of groups defined by backend WURFLModel.
	 * 
	 * @return A Set of String containing the identifier of the groups defined
	 *         by backend WURFLModel.
	 */
	public Set getAllGroups() {

		return model.getAllGroups();
	}

	/**
	 * It is a facade method of WURFLModel#getCapabilitiesForGroup(String). It
	 * returns all capabilities identifier defined by given group in the backend
	 * WURFLModel.
	 * 
	 * @param group
	 *            The identifier of defining group.
	 * @return A Set of String containing the identifiers of the capabilities
	 *         defined by given group.
	 * @throws GroupNotDefinedException
	 *             If the given group is not defined by backend WURFLModel.
	 */
	public Set getCapabilitiesForGroup(final String group)
			throws GroupNotDefinedException {

		Validate.notEmpty(group, "The groupName must be not null");

		return model.getCapabilitiesForGroup(group);

	}

	// Device *************************************************************

	/**
	 * Return a Device instance identified by the given identifier.
	 * 
	 * @param id
	 *            The identifier of Device to get.
	 */
	public Device getDeviceById(String id) throws DeviceNotDefinedException {

		return deviceProvider.getDevice(id);
	}

	/**
	 * Return all the Devices managed by the backend WURFLModel.
	 * 
	 * @return Set of Device.
	 */
	public Set getAllDevices() {

		Set ids = getAllDevicesId();
		Set devices = new HashSet(ids.size());

		for (Iterator idsIt = ids.iterator(); idsIt.hasNext();) {

			try {
				String id = (String) idsIt.next();
				Device device = getDeviceById(id);
				devices.add(device);
			} catch (DeviceNotDefinedException e) {
				throw e;
			}

		}

		return devices;
	}
}
